﻿<!DOCTYPE html>
<html ng-app="directiveApp">
<head>
    <title></title>
    <link href="bootstrap/css/bootstrap.min.css" rel="stylesheet" />
    <script src="scripts/angular.min.js"></script>
    <script>
        angular.module("directiveApp", [])
            .directive("unorderedList", function () {
                return function (scope, element, attrs) {
                    var data = scope[attrs["unorderedList"]];
                    var propertyExpression = attrs["listProperty"];

                    if (angular.isArray(data)) {
                        var listElem = angular.element("<ul>");
                        element.append(listElem);
                        for (var i = 0; i < data.length; i++) {
                            var itemElement = angular.element('<li>');
                            listElem.append(itemElement);
                            /*
                                (1)JavaScript代码的closure特性允许函数内部代码访问在函数外部定义的数据，而不需要将该数据作为参数传递。
                                在本例中，函数function(watchScope)和function(newValue,oldValue)分别访问了在其外部定义的数据：i和itemElement
                                但是，只有在函数被实际调用（而不是声明/定义）时，函数内部的代码才可以真正访问到数据值。
                                (2)function(watchScope)和function(newValue, oldValue)这两个函数，都不会在循环过程中立即调用，
                                而是要到linkFunction调用结束后，才会正式运行watcherFn；而只有当数据发生变化时，才会调用function(newValue, oldValue)
                                因此，如果不使用IIFE，而是直接将代码放在for循环中，那么由于只有1个函数上下文，从而也只有1套index和item。
                                随着循环的进行，index和item的值会被不断修改，循环结束后，index为3，item也只是最后一个<li>元素。
                                此时运行watchFn函数，将只会将index当成3，item当成最后一个<li>元素；
                                也就无法正确进行绑定
                                (3)使用IIFE之后，每个IIFE会自己保存一个上下文。在整个for循环中，会产生3个不同的IIFE上下文，
                                于是index/item分别是各自上下文中对应的数据，不会与其他上下文中的index/item混淆。
                                根据closure特性，这些数据在调用时将会被送到相应的函数中执行。
                                这样，就能完全正常的工作。
                            */
                            (function () {
                                /*
                                    默认情况下，自定义生成的这些元素内容不会随着原先$scope中数据的变化而更新。
                                    使用scope.$watch，将指定的数据(watcherFn返回的内容)加入监视中，
                                    一旦被监视的数据发生变化，就会调用处理函数function(newValue, oldValue)。
                                */
                                var index = i;
                                var item = itemElement;
                                var watcherFn = function (watchScope) {
                                    return watchScope.$eval(propertyExpression, data[index]);
                                }
                                scope.$watch(watcherFn, function (newValue, oldValue) {
                                    item.text(newValue);
                                });
                            }());
                        }
                    }
                }
            })
            .controller("defaultCtrl", function ($scope) {
                $scope.products = [
                    { name: "Apples", category: "Fruit", price: 1.20, expiry: 10 },
                    { name: "Bananas", category: "Fruit", price: 2.42, expiry: 7 },
                    { name: "Pears", category: "Fruit", price: 2.02, expiry: 6 }
                ];

                $scope.incrementPrices = function () {
                    for (var i = 0; i < $scope.products.length; i++) {
                        $scope.products[i].price++;
                    }
                }
            })
    </script>
</head>
<body ng-controller="defaultCtrl">
    <div class="panel panel-default">
        <div class="panel-heading">
            <h3>Products</h3>
        </div>
        <div class="panel-body">
            <button class="btn btn-primary" ng-click="incrementPrices()">
                Change Prices
            </button>
        </div>
        <div class="panel-body">
            <div unordered-list="products" list-property="price | currency"></div>
        </div>
    </div>
</body>
</html>
